﻿// Original author contact info: Owen Emlen (owene_1998@yahoo.com)
// Note: other individuals may also have contributed to this code
// Project hosted on CodePlex.com as of 1/10/2009 at http://wpfimageviewer.codeplex.com
using System;
using System.Linq;
using System.Collections.Generic;
using System.Threading;
using System.Windows.Controls;
using System.Windows.Threading;
using BrainTechLLC;
using BrainTechLLC.WPF;
using ImageAppViewModels;
using System.Windows.Data;
using BrainTechLLC.ThreadSafeObjects;

namespace WpfImageDuplicates
{
    public partial class ImageCollectionView : UserControl
    {
        public SimpleTimer _addTimer;
        public bool _HasSorted;
        public bool _NeedsSort = false;
        public bool _SetOrderedFileNames = false;
        public ThreadSafeListAndLookup<string, KeyFileNameAndOrder> _OrderedFileNames;
        public ThreadSafeLookupNonRef<string, int> _CounterIndexes;
        public int _lastQueueCount;
        public int _lastQueueCountCount;

        protected RunInitializationResult MoveTimerTick(SimpleDispatcherTimer timer)
        {
            if (Window1.MainWindow != null && Window1.MainWindow.ActualWidth > 0 && Displayed._updatedIndexAndOrdering > 0)
            {
                timer.Stop();
                Displayed.UpdateIndexAndOrdering();
                timer.Start();
                return RunInitializationResult.DidSomething;
            }

            return RunInitializationResult.DidntDoAnything;
        }

        protected RunInitializationResult AddTimerTick(SimpleTimer timer)
        {
            if (Window1.MainWindow != null && Window1.MainWindow.ActualWidth > 0)
            {
                if (_OrderedFileNames != null && !_SetOrderedFileNames)
                {
                    SetupFileNameInPositionLookup();
                }

                if (_itemsAdded.Count == 0)
                {
                    if (timer._doneNothingCount > 1 && timer._doneNothingCount < 3 && !_HasSorted && _NeedsSort)
                    {
                        timer.TimerInterval = 200;
                    }
                    else if (timer._doneNothingCount > 2 && !_HasSorted && _NeedsSort)
                    {
                        timer.TimerInterval = 400;
                        PostSort();
                        Thread.Sleep(50);
                    }

                    if (timer._doneNothingCount > 5 && timer.TimerInterval < 200)
                    {
                        timer.TimerInterval = 60;
                    }
                    return RunInitializationResult.DidntDoAnything;
                }

                if (timer.TimerInterval >= 60)
                {
                    timer.TimerInterval = 50;
                }

                if (_itemsAdded.Count == _lastQueueCount && _lastQueueCount > 0)
                {
                    _lastQueueCountCount++;
                }
                else
                {
                    _lastQueueCountCount = 0;
                }

                if (_lastQueueCountCount < 8 && _FileNameInPosition != null && _itemsAdded.Count + Math.Max(_itemsAdded.Count / 20, 1) < ((this._vm == null) ? 0 : this._vm.Count))
                {
                    _lastQueueCount = _itemsAdded.Count;
                    return RunInitializationResult.ContinueTimer;
                }
                // OWEN: Added _itemsAdded.Count == 0 &&
                else if (_itemsAdded.Count == 0 && _FileNameInPosition != null && !_HasSorted)
                {
                    SortInPlaceBasedOnFileNamePositions();
                    return RunInitializationResult.DidSomething;
                }

                try
                {
                    int initialDisplayCount = (int)Math.Ceiling((Window1.MainWindow.ActualHeight / Convert.ToDouble(Settings.Values.ThumbHeight)) * (Window1.MainWindow.ActualWidth / Convert.ToDouble(Settings.Values.ThumbWidth)));

                    if (Displayed.Count < initialDisplayCount)
                    {
                        List<FileImageViewModel> viewModelSpace = new List<FileImageViewModel>(initialDisplayCount);
                        int dequeued = 0;
                        int thisRound = 0;

                        while ((thisRound = _itemsAdded.DequeueMultiple(viewModelSpace, initialDisplayCount)) > 0)
                        {
                            dequeued += thisRound;
                            Dispatcher.RunOnDispatcher(new OneParamDelegate((o) =>
                            {
                                AddViewModels(o);
                            }),
                            DispatcherPriority.Send,
                            new List<FileImageViewModel>(viewModelSpace)
                            );

                            //Console.WriteLine("Sleeping 10");
                            Thread.Sleep(20); // HEREHERE

                            if (dequeued >= initialDisplayCount)
                                break;
                        }
                    }
                    else
                    {
                        // was 300
                        int nextStep = Math.Min(200, Math.Max(_itemsAdded.Count / 6, 50));

                        // More than 50 images displayed
                        List<FileImageViewModel> viewModelSpace = new List<FileImageViewModel>(nextStep);
                        int itemsDequeued = _itemsAdded.DequeueMultiple(viewModelSpace, nextStep);

                        // Could try to not run this on the dispatcher?

                        if (itemsDequeued > 0)
                        {
                            Dispatcher.RunOnDispatcher(new OneParamDelegate((o) =>
                            {
                                AddViewModelsOther(o);
                            }),
                            DispatcherPriority.Background,
                            new List<FileImageViewModel>(viewModelSpace));
                        }
                    }

                    if (_WantCleanupOldThumbs)
                    {
                        Dispatcher.BeginInvoke(() =>
                        {
                            CleanupOldThumbnails();
                            _WantCleanupOldThumbs = false;
                        });
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.ToString());
                }

                return RunInitializationResult.DidSomething;
            }
            else
            {
                return RunInitializationResult.DidntDoAnything;
            }
        }

        private void AddViewModelsOther(object o)
        {
            try
            {
                List<FileImageViewModel> vms = o as List<FileImageViewModel>;

                lock (_displayedLock)
                {
                    if (Source != null)
                    {
                        using (Source.DeferRefresh())
                        {
                            AddRemainingViewModels(vms);
                        }
                    }
                    else
                    {
                        AddRemainingViewModels(vms);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }

        private void AddViewModels(object o)
        {
            try
            {
                List<FileImageViewModel> vms = o as List<FileImageViewModel>;

                lock (_displayedLock)
                {
                    if (Source != null)
                    {
                        using (Source.DeferRefresh())
                        {
                            AddInitialViewModels(vms);
                        }
                    }
                    else
                    {
                        AddInitialViewModels(vms);
                    }
                }

                // may not be needed
                _needForceRefresh = true;
                ForceRefresh = true;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }

        private void SortInPlaceBasedOnFileNamePositions()
        {            
            _NeedsSort = false;
            _HasSorted = true;
            List<FileImageViewModel> all = new List<FileImageViewModel>();
            _itemsAdded.DequeueMultiple(all, _itemsAdded.Count * 2);

            int count = all.Count;

            all.ForEach(o =>
            {
                int idx1 = count;
                _FileNameInPosition.TryGetValue(o.FileName, out idx1);
                o._DesiredOrdering = idx1 + 1;
            });

            all.Sort(new Comparison<FileImageViewModel>((fi1, fi2) => fi1._DesiredOrdering.CompareTo(fi2._DesiredOrdering)));

            Dispatcher.BeginInvoke(() =>
            {
                lock (_displayedLock)
                {
                    if (Source != null)
                    {
                        using (Source.DeferRefresh())
                        {
                            AddInitialViewModels(all);
                        }
                    }
                    else
                    {
                        AddInitialViewModels(all);
                    }
                }

                // may not be needed
                _needForceRefresh = true;
                ForceRefresh = true;

                SetSourceToDisplayed();
            }, DispatcherPriority.Send);

        }

        private void PostSort()
        {
            Dispatcher.BeginInvoke(() =>
            {
                if (_NeedsSort && !_HasSorted && this._vm != null && Displayed != null && this._vm.Count <= Displayed.Count + Math.Max(Displayed.Count / 20, 1))
                {
                    _NeedsSort = false;
                    _HasSorted = true;
                    //SortAll(false);
                    //SetSourceToDisplayed();
                }
            }, DispatcherPriority.Send); // was normal            
        }

        private void SetupFileNameInPositionLookup()
        {
            _SetOrderedFileNames = true;

            string key = DirectoryToExamine == null ? "*" : DirectoryToExamine.Directory;
            var list = Settings.Values.OrderedFileNames[key];

            _FileNameInPosition = new Dictionary<string, int>();
            for (int n = 0; n < list.Count; n++)
            {
                var fileName = list[n];
                if (fileName.Order != n)
                {
                    fileName.Order = n;
                }

                _FileNameInPosition[fileName.FileName] = n;
            }
        }

        private void AddRemainingViewModels(List<FileImageViewModel> vms)
        {
            string key = DirectoryToExamine == null ? "*" : DirectoryToExamine.Directory;
            CheckOrderedFileNames();
            var ordered = _OrderedFileNames[key];
            int counterIndex = _CounterIndexes[key];

            Displayed.SuspendCollectionChanged = true;
            for (int n = 0; n < vms.Count; n++)
            {
                FileImageViewModel vm = vms[n];

                if (vm != null)
                {
                    if (!DisplayedAlready.ContainsKey(vm.FileName))
                    {
                        if (!_NeedsSort && _OrderedFileNames != null)
                        {
                            if (ordered != null)
                            {
                                int foundIndex = counterIndex;
                                if (ordered.Count > foundIndex)
                                {
                                    var fileName = ordered[foundIndex];
                                    if (vm.FileName != fileName.FileName)
                                    {
                                        _NeedsSort = true;
                                    }
                                }
                            }
                        }

                        Displayed.AddItem(vm);
                        counterIndex++;
                        DisplayedAlready.Add(vm.FileName, true);
                    }
                }
            }
            _CounterIndexes[key] = counterIndex;
            Displayed.SuspendCollectionChanged = false;
        }

        private void AddInitialViewModels(List<FileImageViewModel> vms)
        {
            string key = DirectoryToExamine == null ? "*" : DirectoryToExamine.Directory;
            CheckOrderedFileNames();
            var ordered = _OrderedFileNames[key];
            int counterIndex = _CounterIndexes[key];

            for (int n = 0; n < vms.Count; n++)
            {
                FileImageViewModel vm = vms[n];

                if (vm != null)
                {
                    if (!DisplayedAlready.ContainsKey(vm.FileName))
                    {
                        if (!_NeedsSort && _OrderedFileNames != null)
                        {
                            if (ordered != null)
                            {
                                int foundIndex = counterIndex;
                                if (ordered.Count > foundIndex)
                                {
                                    var fileName = ordered[foundIndex];
                                    if (vm.FileName != fileName.FileName)
                                    {
                                        _NeedsSort = true;
                                    }
                                }
                            }
                        }

                        Displayed.AddItem(vm);
                        counterIndex++;
                        DisplayedAlready.Add(vm.FileName, true);
                    }

                    if (!vm.Dirty)
                    {
                        vm.Dirty = true;
                        _needForceRefresh = true;
                    }
                }
            }

            _CounterIndexes[key] = counterIndex;
        }

        private void CheckOrderedFileNames()
        {
            if (_OrderedFileNames == null)
            {
                lock (_lockObject)
                {
                    if (_OrderedFileNames == null)
                    {
                        Interlocked.CompareExchange(ref _OrderedFileNames, new ThreadSafeListAndLookup<string, KeyFileNameAndOrder>(), null);
                    }
                }
            }

            if (_CounterIndexes == null)
            {
                lock (_lockObject)
                {
                    if (_CounterIndexes == null)
                    {
                        _CounterIndexes = new ThreadSafeLookupNonRef<string, int>();
                    }
                }
            }
        }
    }
}